<?php
namespace darksun;
/**
 * Created by PhpStorm.
 * User: darksun
 * Date: 2019/7/16
 * Time: 21:43
 */
use darksun\container\ContainerInterface;

Class Container implements ContainerInterface
{
    protected $bindings = [];

    protected $instances = [];

    protected static $instance;

    protected static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }

        if (static::$instance instanceof \Closure) {
            return (static::$instance)();
        }

        return static::$instance;
    }

    protected static function setInstance($instance): void
    {
        static::$instance = $instance;
    }

    public function bind($abstract, $concrete = null, $shared = false)
    {
        // TODO: Implement bind() method.
        if (is_null($concrete)) {
            $concrete = $abstract;
        }
        if (! $concrete instanceof \Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }elseif(is_object($abstract)){
            $this->instances[$abstract] = $abstract;
            $concrete = $abstract;
        }else{
            $this->bindings[$abstract] = compact('concrete', 'shared');
        }
    }

    public function make($abstract){
        $concrete = $this->getConcrete($abstract);
        $object = $this->build($concrete);
        $this->instances[$concrete] = $object;
        return $object;
    }

    public function build($concrete)
    {
        // TODO: Implement build() method.

        if ($concrete instanceof \Closure){
            return $concrete($this);
        }
        $reflector = new \ReflectionClass($concrete);
        $constructor = $reflector->getConstructor();
        $parameters = $constructor->getParameters();
        $dependencies = $this->resolveDependencies($parameters);
        return $reflector->newInstanceArgs($dependencies);
    }

    public function get($id){
        if(!$this->has($id)){
            throw new \Exception("No entry or class found for {$id}");
        }
        $instance = $this->make($id);
        return $instance;
    }

    public function has($id){
        return isset($this->bindings[$id]) || isset($this->instances[$id]);
    }

    protected function getClosure($abstract, $concrete){
        return function ($container, $parameters = []) use ($abstract, $concrete) {
            if ($abstract == $concrete) {
                return $container->build($concrete);
            }
            return $container->make($concrete, $parameters);
        };
    }

    protected function getConcrete($abstract){

        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        if (isset($this->bindings[$abstract])){
            return $this->bindings[$abstract];
        }
        return $abstract;
    }

    protected function resolveDependencies($parameters){
        $dependencies = [];
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if (! is_null($dependency)) {
                $dependencies[] = $this->resolveClass($parameter);
            }
        }
        return $dependencies;
    }

    protected function resolveClass(\ReflectionParameter $parameter){
        return $this->make($parameter->getClass()->name);
    }



}